package com.gking.centerSystem.service.User;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.gking.centerSystem.common.BaseContext;
import com.gking.centerSystem.common.ProgramException;
import com.gking.centerSystem.common.Result;
import com.gking.centerSystem.entity.dao.PagingQueryUserDao;
import com.gking.centerSystem.entity.*;
import com.gking.centerSystem.mapper.UserMapper;
import com.gking.centerSystem.realm.MyRealm;
import com.gking.centerSystem.realm.MyRealmTwo;
import com.gking.centerSystem.service.Sort.SortService;
import com.gking.centerSystem.serviceUtils.DeepSeek.DeepSeekService;
import com.gking.centerSystem.utils.*;
import com.gking.centerSystem.utils.commonUtil.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.gking.centerSystem.common.RedisConstants.*;

@Slf4j
@Service
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS) //三级缓存解决bean循环注入问题
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private HttpServletRequest request;

    @Resource
    private MyRealm myRealm;

    @Resource
    private MyRealmTwo myRealmTwo;

    @Resource
    private SortService sortService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private DeepSeekService deepSeekService;

    /**
     * 用户注册
     *
     * @param account  账号
     * @param password 密码
     * @return s
     */
    @Override
    public Result<String> regUser(String account, String password) {
        try {
            // 使用AI校验名称是否违规
            String res = deepSeekService.oneTimeSimpleQA(account + "，这个账号名称是否涉政了，或者具有侮辱等不合理性质，你只需要返回 true 或者 false ", "AI校验名称是否违规");
            if (res.equals("true")) return Result.error("账号名称违规");

            // 校验弱密码
            PasswordValidatorUtil.ValidationResult validationResult = PasswordValidatorUtil.validatePassword(password);
            if (!validationResult.isValid()) {
                String msgStr = String.join(",", validationResult.getMessages());
                return Result.error(msgStr);
            }

            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.apply("binary account = {0}", account);
            int count = (int) this.count(wrapper);
            if (count > 0) return Result.error("账号已存在");

            //加密密码之后保存到数据库中
            String hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt(10));

            User user = new User();
            user.setAccount(account);
            user.setPassword(hashedPassword);
            this.save(user);
            return Result.success("注册成功");
        } catch (Exception e) {
            log.error("注册失败： {}", e.getMessage());
            throw new ProgramException("注册失败");
        }
    }

    /**
     * 验证码
     *
     * @param request 线程
     * @return s
     */
    @Override
    public Result<String> verificationCode(HttpServletRequest request) {
        try {
            String verificationCode = RandomGenerator.generateRandomString(4);
            //将验证码存入redis、session中
            stringRedisTemplate.opsForValue().set(Login_Verify_Key + GetAuthInfoUtil.getClientIp(request), verificationCode, Login_Verify_TTL, TimeUnit.MINUTES);
            log.info("verificationCode 验证码： {}", verificationCode);


            VerifyImageUtil verifyImageUtil = new VerifyImageUtil(verificationCode, 100, 50);
            BufferedImage captchaImage = verifyImageUtil.getImage();

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ImageIO.write(captchaImage, "png", outputStream); // 将图像以 PNG 格式写入流
            byte[] imageBytes = outputStream.toByteArray();
            String imageStr = "data:image/png;base64," + java.util.Base64.getEncoder().encodeToString(imageBytes);

            return Result.success("获取验证码成功", imageStr);
        } catch (Exception e) {
            log.error("获取验证码失败： {}", e.getMessage());
            throw new ProgramException("获取验证码失败");
        }
    }

    /**
     * 用户登录
     *
     * @param request          线程
     * @param account          账号
     * @param password         密码
     * @param verificationCode 验证码
     * @return s
     */
    @Override
    public Result<String> login(HttpServletRequest request, String account, String password, String verificationCode) {
        try {
            // 验证码
            String cacheCode = stringRedisTemplate.opsForValue().get(Login_Verify_Key + GetAuthInfoUtil.getClientIp(request));
            stringRedisTemplate.delete(Login_Verify_Key + GetAuthInfoUtil.getClientIp(request));
            if (!verificationCode.equalsIgnoreCase(cacheCode)) return Result.error("验证码错误");
            // if (!verificationCode.equals(cacheCode)) return Result.error("验证码错误");

            // 获取数据库用户密码
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.apply("binary account = {0}", account);
            lambdaQueryWrapper.apply("binary enable = {0}", 0);
            User user = this.getOne(lambdaQueryWrapper);
            if (user == null) return Result.error("账号不存在");

            // 检查登录密码是否正确，密码错误则返回
            boolean passwordMatch = BCrypt.checkpw(password, user.getPassword());
            log.info("密码校验是否正确： {}", passwordMatch);
            if (!passwordMatch) return Result.error("密码错误");

            String loginUserKey = GetAuthInfoUtil.setUserKey(cacheCode);
            String encryptedToken = CustomEncryptorUtil.encryption(loginUserKey);
            String loginTokenJwt = JwtUtil.createToken(encryptedToken);


            //shiro  框架
            Subject subject = SecurityUtils.getSubject();
            //2 封装请求数据到 token
            AuthenticationToken t = new UsernamePasswordToken(user.getId() + cacheCode, user.getPassword(), true);
            subject.login(t);

            String deviceId = GetAuthInfoUtil.getClientDeviceInfo(request);
            String ip = GetAuthInfoUtil.getClientIp(request);

            GetAuthInfoUtil getAuthInfoUtil = new GetAuthInfoUtil(stringRedisTemplate, request);
            getAuthInfoUtil.setUserCacheInfo(loginUserKey, user.getId(), deviceId, ip);


            return Result.success("登录成功", loginTokenJwt);
        } catch (AuthenticationException e) {
            log.error("shiro 登录失败！ {} ", e.getMessage());
            throw new ProgramException("登录失败！");
        }

    }


    /**
     * 获取用户登录缓存的凭证信息
     *
     * @return s
     */
    public Result<Map> getUserLoginAuth(String equipment, String ip) {
        if (ip == null || ip.isEmpty()) return Result.error("请将参数补充完整！");

        try {
            GetAuthInfoUtil getAuthInfoUtil = new GetAuthInfoUtil(stringRedisTemplate, request);
            String userDeviceInfo = getAuthInfoUtil.getUserDeviceInfo();
            String userServerInfo = getAuthInfoUtil.getUserServerInfo();
            String userLoginIp = getAuthInfoUtil.getUserLoginIp();

            log.info("userDeviceInfo 设备信息： {}", userDeviceInfo);
            log.info("userServerInfo 用户id： {}", userServerInfo);
            log.info("userLoginIp 用户ip： {}", userLoginIp);
            log.info("ip {} , equipment {}", ip, equipment);

            if (!Objects.equals(equipment, userDeviceInfo) || !Objects.equals(ip, userLoginIp))
                return Result.error("设备信息不匹配");

            Map<String, String> map = new HashMap<>();
            map.put("userId", userServerInfo);
            map.put("ip", userLoginIp);
            map.put("device", userDeviceInfo);

            // TODO
            return Result.success("获取成功", map);
        } catch (Exception e) {
            log.error("获取用户登录缓存的凭证信息失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }

    /**
     * 退出登录
     *
     * @return s
     */
    @Override
    public Result<String> logout() {
        myRealm.clearCached();
        myRealmTwo.clearCached();

        GetAuthInfoUtil getAuthInfoUtil = new GetAuthInfoUtil(stringRedisTemplate, request);
        getAuthInfoUtil.clearUserCacheInfo();

        return Result.success("退出登录成功！");
    }


    /**
     * 获取用户信息
     *
     * @return s
     */
    @Override
    public Result<User> getUserInfo() {
        String userId = BaseContext.getCurrentId();
        User user = this.getById(userId);
        user.setPassword(null);
        user.setEmail(CustomEncryptorUtil.decrypt(user.getEmail()));
        user.setPhoneNumber(CustomEncryptorUtil.decrypt(user.getPhoneNumber()));
        return Result.success("获取用户信息成功", user);
    }

    /**
     * 修改用户信息
     *
     * @param nickname    昵称
     * @param phoneNumber 手机号
     * @param email       邮箱
     * @param signature   邮箱
     * @return s
     */
    @Override
    public Result<String> updateUserInfo(String nickname, String phoneNumber, String email, String signature) {

        if (nickname.isEmpty()) return Result.error("请将参数补充完整");

        try {
            String userId = BaseContext.getCurrentId();
            User user = new User();
            user.setId(userId);
            user.setNickname(nickname);
            user.setSignature(signature);
            user.setPhoneNumber(CustomEncryptorUtil.encryption(phoneNumber));
            user.setEmail(CustomEncryptorUtil.encryption(email));
            user.setPhoneFuzzyQuery(CustomEncryptorUtil.encryptionSplitString(phoneNumber, 3));
            user.setEmailFuzzyQuery(CustomEncryptorUtil.encryptionSplitString(email, 3));

            this.updateById(user);
            return Result.success("修改用户信息成功");
        } catch (Exception e) {
            log.error("修改用户信息失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }


    /**
     * 通过一个分类id找出该分类下的所有分类id
     *
     * @param id 分类id
     * @return s
     */
    private List<String> getSortIds(String id, String type) {
        List<String> idList = new ArrayList<>();
        idList.add(id);

        // 将分类下的所有分类id查找出来
        LambdaQueryWrapper<Sort> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Sort::getParentId, id);
        queryWrapper.eq(Sort::getIsDelete, 0);
        queryWrapper.eq(Sort::getType, type);
        List<Sort> sorts = sortService.list(queryWrapper);

        // 递归找出所有id
        for (Sort sort : sorts) idList.addAll(getSortIds(sort.getId(), type));
        return idList;
    }

    /**
     * 获取用户可以访问的页面路由
     *
     * @param systemName 系统名称
     * @return s
     */
    @Override
    public Result<List<Routes>> getUserAccessRoutes(String systemName) {

        if (systemName.isEmpty()) return Result.error("请将参数补充完整");

        try {
            final String userId = BaseContext.getCurrentId();
            final String verify = BaseContext.getLoginVerifyCode();

            // 获取哪个系统的分类
            LambdaQueryWrapper<Sort> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Sort::getDescription, systemName);
            queryWrapper.eq(Sort::getIsDelete, 0);
            queryWrapper.eq(Sort::getLevel, 1);
            Sort systemRootSort = sortService.getOne(queryWrapper);


            // 提取redis中用户该系统路由列表，没有则重新获取
            String systemRoutesJsonString = stringRedisTemplate.opsForValue()
                    .get(Login_User_Routes + userId + verify + ":System_" + systemRootSort.getId());
            if (systemRoutesJsonString != null) {
                Type type = new TypeReference<List<Routes>>() {
                }.getType();
                List<Routes> routesList = JSON.parseObject(systemRoutesJsonString, type);
                return Result.success("获取用户系统路由列表成功", routesList);
            }

            // 获取用户该系统路由列表，通过角色获取路由id
            List<String> sorts = getSortIds(systemRootSort.getId(), "role");
            List<Routes> l = userMapper.getUserAccessRoutes(userId, sorts);
            if (l != null) {
                // 将用户的该系统的路由表存入redis缓存中
                stringRedisTemplate.opsForValue().set(
                        Login_User_Routes + userId + verify + ":System_" + systemRootSort.getId(),
                        JSON.toJSONString(l), Login_User_Info_TTL, TimeUnit.HOURS);

                return Result.success("获取用户系统路由列表成功", l);
            } else return Result.error("获取用户系统路由列表失败");

        } catch (Exception e) {
            log.error("获取用户系统路由列表失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }


    /**
     * 分页条件查询用户信息
     *
     * @param page               分页页码
     * @param size               分页大小
     * @param pagingQueryUserDao 分页查询用户的一些条件
     * @return s
     */
    @Override
    public Result<Page> queryPageUserList(Integer page, Integer size, PagingQueryUserDao pagingQueryUserDao) {

        if (page == null || size == null) return Result.error("请将参数补充完整");

        try {
            Page<User> pageInfo = new Page<>(page, size);
            String roleName = pagingQueryUserDao.getRoleName();
            String account = pagingQueryUserDao.getAccount();
            String userName = pagingQueryUserDao.getNickname();
            String phone = pagingQueryUserDao.getPhoneNumber();
            String email = pagingQueryUserDao.getEmail();
            Integer enable = pagingQueryUserDao.getEnable();

            IPage<User> userIPage = userMapper.findUsersByCriteria(pageInfo, roleName, account, userName,
                    CustomEncryptorUtil.encryptionSplitStringToList(phone, 3),
                    CustomEncryptorUtil.encryptionSplitStringToList(email, 3),
                    enable);

            List<User> records = new ArrayList<>();
            for (User user : userIPage.getRecords()) {
                user.setPhoneNumber(CustomEncryptorUtil.decrypt(user.getPhoneNumber()));
                user.setEmail(CustomEncryptorUtil.decrypt(user.getEmail()));
                records.add(user);
            }

            Page<User> pageResult = (Page<User>) userIPage;
            pageResult.setRecords(records);

            return Result.success("获取用户列表成功！", pageResult);
        } catch (Exception e) {
            log.error("获取用户列表失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }

    @Override
    public Result<Page> queryPageUserByEasy(Integer page, Integer size, String account, String nickname) {
        if (page == null || size == null) return Result.error("请将参数补充完整");

        try {
            Page<User> pageInfo = new Page<>(page, size);
            IPage<User> userIPage = userMapper.findUsersByEasy(pageInfo, account, nickname);

            List<User> records = new ArrayList<>();
            for (User user : userIPage.getRecords()) {
//                user.setPhoneNumber(CustomEncryptorUtil.decrypt(user.getPhoneNumber()));
//                user.setEmail(CustomEncryptorUtil.decrypt(user.getEmail()));
                user.setPhoneNumber(null);
                user.setEmail(null);
                records.add(user);
            }

            Page<User> pageResult = (Page<User>) userIPage;
            pageResult.setRecords(records);

            return Result.success("获取用户列表成功！", pageResult);
        } catch (Exception e) {
            log.error("获取用户列表失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }


    /**
     * 设置用户状态
     *
     * @param userIds 用户id
     * @param status  状态 0 、1
     * @return s
     */
    @Override
    public Result<String> setUserStatus(List<String> userIds, String status) {

        if (userIds.isEmpty()) return Result.error("请将参数补充完整");
        if (!status.equals("0") && !status.equals("1")) return Result.error("状态参数错误");

        try {
            LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
            wrapper.in(User::getId, userIds);
            wrapper.set(User::getEnable, status);
            this.update(wrapper);
            return Result.success("更新用户状态成功！");
        } catch (Exception e) {
            log.error("更新用户状态失败！ {} ", e.getMessage());
            throw new ProgramException();
        }
    }


}
